package com.peng.mybatis.session;

import com.peng.mybatis.binding.MapperRegistry;
import com.peng.mybatis.datasource.druid.DruidDataSourceFactory;
import com.peng.mybatis.datasource.pooled.PooledDataSourceFactory;
import com.peng.mybatis.datasource.unpooled.UnpooledDataSourceFactory;
import com.peng.mybatis.executor.Executor;
import com.peng.mybatis.executor.SimpleExecutor;
import com.peng.mybatis.executor.resultset.DefaultResultSetHandler;
import com.peng.mybatis.executor.resultset.ResultSetHandler;
import com.peng.mybatis.executor.statement.PreparedStatementHandler;
import com.peng.mybatis.executor.statement.StatementHandler;
import com.peng.mybatis.mapping.BoundSql;
import com.peng.mybatis.mapping.Environment;
import com.peng.mybatis.mapping.MappedStatement;
import com.peng.mybatis.reflection.MetaObject;
import com.peng.mybatis.reflection.factory.DefaultObjectFactory;
import com.peng.mybatis.reflection.factory.ObjectFactory;
import com.peng.mybatis.reflection.wrapper.DefaultObjectWrapperFactory;
import com.peng.mybatis.reflection.wrapper.ObjectWrapperFactory;
import com.peng.mybatis.scripting.LanguageDriverRegistry;
import com.peng.mybatis.transaction.Transaction;
import com.peng.mybatis.transaction.jdbc.JdbcTransactionFactory;
import com.peng.mybatis.type.TypeAliasRegistry;
import com.peng.mybatis.type.TypeHandlerRegistry;
import lombok.Getter;
import lombok.Setter;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 核心配置类，贯穿mybatis整个流程的核心类
 */
public class Configuration {

    @Getter
    @Setter
    protected Environment environment;

    /**
     * mapper代理工厂注册器
     */
    protected MapperRegistry mapperRegistry = new MapperRegistry(this);

    /**
     * 缓存MappedStatement
     */
    protected Map<String, MappedStatement> mappedStatements = new ConcurrentHashMap<>();

    @Getter
    protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();

    /**
     * 别名映射器
     */
    @Getter
    protected TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();

    protected ObjectFactory objectFactory = new DefaultObjectFactory();

    protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();

    protected final Set<String> loadedResources = new HashSet<>();

    @Getter
    protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();

    @Getter
    protected String databaseId;

    public Configuration() {
        typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
        typeAliasRegistry.registerAlias("DRUID", DruidDataSourceFactory.class);
        typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
        typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    }

    public void addMapper(Class<?> mapperInterface) {
        mapperRegistry.addMapper(mapperInterface);
    }

    public void addMappers(String packageName) {
        mapperRegistry.addMappers(packageName);
    }

    public <T> T getMapper(Class<T> mapperInterface, SqlSession sqlSession) {
        return mapperRegistry.getMapper(mapperInterface, sqlSession);
    }

    public void addMappedStatement(MappedStatement mappedStatement) {
        mappedStatements.put(mappedStatement.getId(), mappedStatement);
    }

    public MappedStatement getMappedStatement(String id) {
        return mappedStatements.get(id);
    }

    public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, BoundSql boundSql) {
        return new DefaultResultSetHandler(executor, mappedStatement, boundSql);
    }

    public Executor newExecutor(Transaction transaction) {
        return new SimpleExecutor(this, transaction);
    }

    public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter,
                                                ResultHandler resultHandler, BoundSql boundSql) {
        return new PreparedStatementHandler(executor, mappedStatement, parameter, resultHandler, boundSql);
    }

    public boolean isResourceLoaded(String resource) {
        return loadedResources.contains(resource);
    }

    public void addLoadedResource(String resource) {
        loadedResources.add(resource);
    }

    public MetaObject newMetaObject(Object object) {
        return MetaObject.forObject(object, objectFactory, objectWrapperFactory);
    }

}
